package com.demo.aop;

import com.demo.annotation.ShardingStrategy;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.apache.shardingsphere.api.hint.HintManager;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.Signature;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.context.annotation.Configuration;

import java.util.zip.CRC32;

/**
 * 分片注解aop
 */
@Slf4j
@Aspect
@Configuration
public class ShardingStrageyAop {

    @Pointcut("@annotation(com.demo.annotation.ShardingStrategy)")
    public void addAdvice() {}

    @Around("addAdvice()")
    public Object interceptor(ProceedingJoinPoint pjp) throws Throwable {
        Signature signature = pjp.getSignature();
        Object[] args = pjp.getArgs();
        MethodSignature methodSignature = (MethodSignature) signature;
        // 通过这获取到方法的所有参数名称的字符串数组
        String[] parameterNames = methodSignature.getParameterNames();
        ShardingStrategy shardingStrategy = methodSignature.getMethod().getAnnotation(ShardingStrategy.class);
        if (StringUtils.isBlank(shardingStrategy.shardingColumn()) && StringUtils.isBlank(shardingStrategy.dbIndex())) {
            throw new IllegalArgumentException("ShardingStrategy注解shardingColumn和dbIndex不能同时为空");
        }

        String databaseName = null;
        if (StringUtils.isNotBlank(shardingStrategy.shardingColumn())) {
            for (int i = 0; i < parameterNames.length; i++) {
                if (StringUtils.equals(parameterNames[i], shardingStrategy.shardingColumn())) {
                    long mod = 0;
                    if (args[i] instanceof String) {
                        CRC32 crc32 = new CRC32();
                        crc32.update(args[i].toString().getBytes());
                        crc32.getValue();
                    } else if (args[i] instanceof Integer || args[i] instanceof Long) {
                        mod = ((Number) args[i]).longValue() % 2;
                    }

                    databaseName = "ds" + mod % 2;
                    log.info("shardingColumn分片值：{}，数据库：{}", args[i], databaseName);
                    break;
                }
            }
        } else {
            for (int i = 0; i < parameterNames.length; i++) {
                if (StringUtils.equals(parameterNames[i], shardingStrategy.dbIndex())) {
                    long dbIndex = (long) args[i];
                    databaseName = "ds" + dbIndex;
                    log.info("dbIndex分片值：{}，数据库：{}", dbIndex, databaseName);
                    break;
                }
            }
        }

        if (StringUtils.isBlank(databaseName)) {
            throw new IllegalArgumentException("ShardingStrategy注解属性匹配不到入参参数");
        }

        try (HintManager instance = HintManager.getInstance()) {
            instance.setDatabaseShardingValue(databaseName);
//            HintManagerHolder.clear();
//            HintManagerHolder.setHintManager(instance);
            return pjp.proceed();
        }
    }
}
